package cn.shiwei.user.service.impl;

import cn.shiwei.basic.constants.UserConstants;
import cn.shiwei.basic.exception.MyException;
import cn.shiwei.basic.util.HttpClientUtils;
import cn.shiwei.basic.util.MD5Utils;
import cn.shiwei.basic.util.RequestStatus;
import cn.shiwei.basic.util.StrUtils;
import cn.shiwei.user.domain.LoginDto;
import cn.shiwei.user.domain.Logininfo;
import cn.shiwei.user.domain.User;
import cn.shiwei.user.domain.WXUser;
import cn.shiwei.user.mapper.LogininfoMapper;
import cn.shiwei.user.mapper.UserMapper;
import cn.shiwei.user.mapper.WXUserMapper;
import cn.shiwei.user.service.ILoginService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private WXUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public RequestStatus login(LoginDto loginDto) {

        String password = loginDto.getPassword();
        Integer type = loginDto.getType();
        String username = loginDto.getUsername();

        // 非空判断
        if (StringUtils.isEmpty(username)
                || StringUtils.isEmpty(password)
                || StringUtils.isEmpty(type)
                ) {
            throw new MyException("手机号/密码 不能为空");
        }

        // 根据用户名和type查询用户信息
        Logininfo logininfo = logininfoMapper.findByNameAndType(loginDto);
        if (logininfo == null) {
            // 用户名错误
            throw new MyException("用户名错误!");
        }

        // 比对密码
        String md5 = MD5Utils.encrypByMd5(password);
        String tmpPassword = logininfo.getSalt() + md5;

        if (!tmpPassword.equals(logininfo.getPassword())) {
            // 密码错误
            throw new MyException("密码错误!");
        }

        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo,30, TimeUnit.MINUTES);

        // 发前端前屏蔽关键信息
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        logininfo.setType(null);

        // 返回前端teken和用户信息
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);

        return RequestStatus.me().setResultObj(map);
    }

    @Override
    public RequestStatus weChatLogin(Map<String, String> map) {
        String code = map.get("code");
        // 判断code非空
        if (StringUtils.isEmpty(code)) {
            throw new MyException("授权失败, 请重新扫码");
        }

        // 通过code获取token(微信的授权令牌)
        String strObj = HttpClientUtils.httpGet(
                UserConstants.TOKEN_BY_CODE
                        .replace("APPID", UserConstants.APPID)
                        .replace("SECRET", UserConstants.SECRET)
                        .replace("CODE", code)
        );


        // 处理获得得json字符串
        JSONObject jsonObject = JSONObject.parseObject(strObj);
        // 获取token 令牌 授权调用凭证
        String access_token = jsonObject.getString("access_token");
        // 获取openid 用户授权标识
        String openid = jsonObject.getString("openid");
        // 根据openid查询用户信息
        WXUser wxUser = wxUserMapper.loadByOpenId(openid);
        // 判断wxUser且wxuser表中user_id是否为都不为空(免密登录, 已有的用户登录)
        if (wxUser != null && wxUser.getUser().getId() != null) {
            // 表名登录成功, 向redis中存入相关logininfo信息
            // 通过user_id查询logininfo
            Logininfo logininfo = logininfoMapper.findByUserId(wxUser.getUser().getId());
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set(token, logininfo,30, TimeUnit.MINUTES);

            // 发前端前屏蔽关键信息
            logininfo.setSalt(null);
            logininfo.setPassword(null);
            logininfo.setType(null);

            // 返回前端teken和用户信息
            Map<String, Object> resultObj = new HashMap<>();
            resultObj.put("token", token);
            resultObj.put("logininfo", logininfo);
            return RequestStatus.me().setResultObj(resultObj);
        } else { // 没有用微信扫描过
            String param = "?accessToken=" + access_token + "&openId=" + openid;
            return RequestStatus.me().setMessage("未绑定").setResultObj(param);
        }


    }

    @Override
    public Map<String, Object> binder(Map<String, String> param) {
        // 四大参数校验
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String phone = param.get("phone");
        String openId = param.get("openId");
        if (StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(accessToken)
                || StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(openId)
                ){
            throw new MyException("手机号和验证码不能为空");
        }
        // 验证码校验
        // 获取key
        String verificationCode = UserConstants.BINDER_CODE_PREFIX.concat(phone);
        // 获取value
        String code = (String)redisTemplate.opsForValue().get(verificationCode);

        // 校验验证码过期
        if (code == null){
            throw new MyException("验证码失效");
        } else {
            code = code.substring(code.indexOf(":") + 1);
            // 用户输入的验证码与服务器的不一致
            if (!code.equalsIgnoreCase(verifyCode)){
                System.out.println("服务器查出的" + code);
                System.out.println("用户输入的" + verifyCode);
                throw new MyException("验证码错误");
            }
        }

        // 手机号是否注册?注册直接绑定:新建
        User tmpUser = userMapper.findByPhone(phone);
        User user = null;
        Logininfo logininfo = null;
        if (tmpUser != null){
            // 绑定的手机号对应的用户存在, 直接绑定
            user = tmpUser;
            logininfo = logininfoMapper.findByUserId(tmpUser.getId());
        } else {
            // 生成user , logininfo, wxuser
            user = initUser(phone);
            logininfo = initLogininfo(phone);
            // 先保存logininfo , 返回主键
            logininfoMapper.save(logininfo);
            user.setLogininfo(logininfo);
            userMapper.save(user);
        }

        // 获取微信授权用户信息
        String wxUserUrl = UserConstants.WX_GET_USER
                .replace("APPID",  UserConstants.APPID)
                .replace("ACCESS_TOKEN", accessToken);
        String wx_user = HttpClientUtils.httpGet(wxUserUrl);

        // 获取wxuser对象, 保存
        WXUser wxUser = initWXUser(wx_user, user);
        wxUserMapper.save(wxUser);

        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, logininfo,30, TimeUnit.MINUTES);

        // 发前端前屏蔽关键信息
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        logininfo.setType(null);

        // 返回前端teken和用户信息
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);
        return map;
    }

    /**
     * 根据微信用户的个人信息, 生成wxuser对象
     * @param wx_user
     * @param user
     * @return
     */
    private WXUser initWXUser(String wx_user, User user) {
        JSONObject jsonObject = JSONObject.parseObject(wx_user);
        WXUser wxUser = new WXUser();
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setAddress(jsonObject.getString("city"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        wxUser.setUser(user);
        return wxUser;
    }

    /**
     * 根据phone 生成logininfo对象
     * @param phone
     * @return
     */
    private Logininfo initLogininfo(String phone) {
        // 初始密码就是手机号后6位
        return new Logininfo(phone, phone, UserConstants.USER_TYPE);
    }

    /**
     * 通过手机号生成一个user对象
     * @param phone
     * @return
     */
    private User initUser(String phone) {
        // 初始密码就是手机号后6位
        return new User(phone, phone, UserConstants.USER_STATE_ENABLE);
    }
}
